using Xamarin.SourceWriter;

namespace Xamarin.Android.Tools.ManifestAttributeCodeGenerator;

// This is the common data class used by both Mono.Android and Xamarin.Android.Build.Tasks.
class AttributeDataClass : ClassWriter
{
	AttributeMappingField? mapping_field;
	AttributeMappingStaticConstructor? static_constructor;

	static AttributeMappingManualInitializer manual_mapping_partial = AttributeMappingManualInitializer.Create ();

	public string Namespace { get; set; }

	public AttributeDataClass (string ns)
	{
		Namespace = ns;
	}

	public static AttributeDataClass Create (ElementDefinition attr, MetadataSource metadata, MetadataType type)
	{
		var c = new AttributeDataClass (type.Namespace) {
			Name = type.ManagedName,
			IsPublic = true,
			IsPartial = true,
			IsSealed = type.IsSealed,
			Inherits = "Attribute",
		};

		if (type.IsJniNameProvider)
			c.Implements.Add ("Java.Interop.IJniNameProviderAttribute");

		c.Attributes.Add (new SerializableAttr ());
		c.Attributes.Add (new AttributeUsageAttr (type));

		// Add empty constructor
		if (type.HasDefaultConstructor)
			c.Constructors.Add (new ConstructorWriter {
				Name = c.Name,
				IsPublic = true,
			});

		// Add properties
		foreach (var a in attr.Attributes.OrderBy (a => a.Name)) {
			var attr_metadata = metadata.GetMetadata ($"{attr.ActualElementName}.{a.Name}");

			if (!attr_metadata.Visible)
				continue;

			var p = new PropertyWriter {
				Name = (attr_metadata.Name ?? a.Name).Capitalize (),
				PropertyType = new TypeReferenceWriter (attr_metadata.Type ?? a.GetAttributeType ()),
				IsPublic = true,
				HasGet = true,
				HasSet = true,
				IsAutoProperty = true,
				AutoSetterVisibility = attr_metadata.ReadOnly ? Visibility.Private : Visibility.Public,
			};

			// Add obsolete attribute
			if (attr_metadata.Obsolete is not null)
				p.Attributes.Add (new ObsoleteAttr (attr_metadata.Obsolete));

			c.Properties.Add (p);
		}

		// Create mapping field used by Xamarin.Android.Build.Tasks
		if (type.GenerateMapping) {
			c.mapping_field = AttributeMappingField.Create (type);
			c.static_constructor = AttributeMappingStaticConstructor.Create (attr, metadata, type);
		}

		return c;
	}

	public override void Write (CodeWriter writer)
	{
		writer.WriteAutoGeneratedHeader ();
		writer.WriteLine ();

		writer.WriteLine ("using System;");
		writer.WriteLine ();
		writer.WriteLine ($"namespace {Namespace};");
		writer.WriteLine ();

		base.Write (writer);
	}

	public override void WriteMembers (CodeWriter writer)
	{
		base.WriteMembers (writer);

		if (mapping_field is not null) {
			writer.WriteLineNoIndent ("#if XABT_MANIFEST_EXTENSIONS");

			mapping_field?.Write (writer);
			writer.WriteLine ();

			static_constructor?.Write (writer);
			writer.WriteLine ();

			manual_mapping_partial?.Write (writer);

			writer.WriteLineNoIndent ("#endif // XABT_MANIFEST_EXTENSIONS");
		}
	}
}
